package com.dc.androidutilcode.utils;

import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件流工具类
 */
public class FileIOUtils {

    private static final int BUFFER_SIZE = 1024 * 1024;

    /* ********************** writeFileFromIS *********************/

    /**
     * 将输入流写入文件
     *
     * @param filePath 文件路径
     * @param is       输入流
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is) {
        return writeFileFromIS(FileUtils.getFileByPath(filePath), is, false, null);
    }

    /**
     * 将输入流写入文件
     *
     * @param filePath 文件路径
     * @param is       输入流
     * @param append   是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is, final boolean append) {
        return writeFileFromIS(FileUtils.getFileByPath(filePath), is, append, null);
    }

    /**
     * 将输入流写入文件
     *
     * @param file 文件
     * @param is   输入流
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final File file, final InputStream is) {
        return writeFileFromIS(file, is, false, null);
    }

    /**
     * 将输入流写入文件
     *
     * @param file   文件
     * @param is     输入流
     * @param append 是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final File file, final InputStream is, final boolean append) {
        return writeFileFromIS(file, is, append, null);
    }

    /**
     * 将输入流写入文件
     *
     * @param filePath 文件路径
     * @param is       输入流
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is, final OnProgressUpdateListener listener) {
        return writeFileFromIS(FileUtils.getFileByPath(filePath), is, false, listener);
    }

    /**
     * 将输入流写入文件
     *
     * @param filePath 文件路径
     * @param is       输入流
     * @param append   是否追加数据
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is, final boolean append, final OnProgressUpdateListener listener) {
        return writeFileFromIS(FileUtils.getFileByPath(filePath), is, append, listener);
    }

    /**
     * 输入流写入文件
     *
     * @param file     文件
     * @param is       输入流
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final File file, final InputStream is, final OnProgressUpdateListener listener) {
        return writeFileFromIS(file, is, false, listener);
    }

    /**
     * 将输入流写入文件
     *
     * @param file     文件
     * @param is       输入流
     * @param append   是否追加数据
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromIS(final File file, final InputStream is, final boolean append, final OnProgressUpdateListener listener) {
        if (is == null || !FileUtils.createOrExistsFile(file)) {
            Log.e("FileIOUtils", "create file <" + file + "> failed.");
            return false;
        }
        OutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(file, append), BUFFER_SIZE);
            if (listener == null) {
                byte[] data = new byte[BUFFER_SIZE];
                for (int len; (len = is.read(data)) != -1; ) {
                    os.write(data, 0, len);
                }
            } else {
                double totalSize = is.available();
                int curSize = 0;
                listener.onProgressUpdate(0);
                byte[] data = new byte[BUFFER_SIZE];
                for (int len; (len = is.read(data)) != -1; ) {
                    os.write(data, 0, len);
                    curSize += len;
                    listener.onProgressUpdate(curSize / totalSize);
                }
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /* ********************** writeFileFromBytesByStream *********************/

    /**
     * 将字节数组写入文件
     *
     * @param filePath 文件路径
     * @param bytes    字节数组
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) {
        return writeFileFromBytesByStream(FileUtils.getFileByPath(filePath), bytes, false, null);
    }

    /**
     * 将字节数组写入文件
     *
     * @param filePath 文件路径
     * @param bytes    字节数组
     * @param append   是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) {
        return writeFileFromBytesByStream(FileUtils.getFileByPath(filePath), bytes, append, null);
    }

    /**
     * 将字节数组写入文件
     *
     * @param file  文件
     * @param bytes 字节数组
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) {
        return writeFileFromBytesByStream(file, bytes, false, null);
    }

    /**
     * 将字节数组写入文件
     *
     * @param file   文件
     * @param bytes  字节数组
     * @param append 是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) {
        return writeFileFromBytesByStream(file, bytes, append, null);
    }

    /**
     * 将字节数组写入文件
     *
     * @param filePath 文件路径
     * @param bytes    字节数组
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final OnProgressUpdateListener listener) {
        return writeFileFromBytesByStream(FileUtils.getFileByPath(filePath), bytes, false, listener);
    }

    /**
     * 将字节数组写入文件
     *
     * @param filePath 文件路径
     * @param bytes    字节数组
     * @param append   是否追加数据
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append, final OnProgressUpdateListener listener) {
        return writeFileFromBytesByStream(FileUtils.getFileByPath(filePath), bytes, append, listener);
    }

    /**
     * 将字节数组写入文件
     *
     * @param file     文件
     * @param bytes    字节数组
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final OnProgressUpdateListener listener) {
        return writeFileFromBytesByStream(file, bytes, false, listener);
    }

    /**
     * 将字节数组写入文件
     *
     * @param file     文件
     * @param bytes    字节数组
     * @param append   是否追加数据
     * @param listener 进度更新回调
     * @return 是否写入成功
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append, final OnProgressUpdateListener listener) {
        if (bytes == null) {
            return false;
        }
        return writeFileFromIS(file, new ByteArrayInputStream(bytes), append, listener);
    }

    /* ********************** writeFileFromString *********************/

    /**
     * 将字符串写入文件
     *
     * @param filePath 文件路径
     * @param content  字符串
     * @return 是否写入成功
     */
    public static boolean writeFileFromString(final String filePath, final String content) {
        return writeFileFromString(FileUtils.getFileByPath(filePath), content, false);
    }

    /**
     * 将字符串写入文件
     *
     * @param filePath 文件路径
     * @param content  字符串
     * @param append   是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromString(final String filePath, final String content, final boolean append) {
        return writeFileFromString(FileUtils.getFileByPath(filePath), content, append);
    }

    /**
     * 将字符串写入文件
     *
     * @param file    文件
     * @param content 字符串
     * @return 是否写入成功
     */
    public static boolean writeFileFromString(final File file, final String content) {
        return writeFileFromString(file, content, false);
    }

    /**
     * 将字符串写入文件
     *
     * @param file    文件
     * @param content 字符串
     * @param append  是否追加数据
     * @return 是否写入成功
     */
    public static boolean writeFileFromString(final File file, final String content, final boolean append) {
        if (file == null || content == null) {
            return false;
        }
        if (!FileUtils.createOrExistsFile(file)) {
            Log.e("FileIOUtils", "create file <" + file + "> failed.");
            return false;
        }
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(file, append));
            bw.write(content);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /* ********************** readFile2List *********************/

    /**
     * 读取文件到字符串链表中
     *
     * @param filePath 文件路径
     * @return List
     */
    public static List<String> readFile2List(final String filePath) {
        return readFile2List(FileUtils.getFileByPath(filePath), null);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param filePath    文件路径
     * @param charsetName 字符集名称
     * @return List
     */
    public static List<String> readFile2List(final String filePath, final String charsetName) {
        return readFile2List(FileUtils.getFileByPath(filePath), charsetName);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param file 文件
     * @return List
     */
    public static List<String> readFile2List(final File file) {
        return readFile2List(file, 0, 0x7FFFFFFF, null);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param file        文件
     * @param charsetName 字符集名称
     * @return List
     */
    public static List<String> readFile2List(final File file, final String charsetName) {
        return readFile2List(file, 0, 0x7FFFFFFF, charsetName);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param filePath 文件路径
     * @param st       行的起始索引
     * @param end      行的结束索引
     * @return List
     */
    public static List<String> readFile2List(final String filePath, final int st, final int end) {
        return readFile2List(FileUtils.getFileByPath(filePath), st, end, null);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param filePath    文件路径
     * @param st          行的起始索引
     * @param end         行的结束索引
     * @param charsetName 字符集名称
     * @return List
     */
    public static List<String> readFile2List(final String filePath, final int st, final int end, final String charsetName) {
        return readFile2List(FileUtils.getFileByPath(filePath), st, end, charsetName);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param file 文件
     * @param st   行的起始索引
     * @param end  行的结束索引
     * @return List
     */
    public static List<String> readFile2List(final File file, final int st, final int end) {
        return readFile2List(file, st, end, null);
    }

    /**
     * 读取文件到字符串链表中
     *
     * @param file        文件
     * @param st          行的起始索引
     * @param end         行的结束索引
     * @param charsetName 字符集名称
     * @return List
     */
    public static List<String> readFile2List(final File file, final int st, final int end, final String charsetName) {
        if (!FileUtils.isFileExists(file)) {
            return null;
        }
        if (st > end) {
            return null;
        }
        BufferedReader reader = null;
        try {
            String line;
            int curLine = 1;
            List<String> list = new ArrayList<>();
            if (StringUtils.isSpace(charsetName)) {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            } else {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
            }
            while ((line = reader.readLine()) != null) {
                if (curLine > end) {
                    break;
                }
                if (st <= curLine && curLine <= end) {
                    list.add(line);
                }
                ++curLine;
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /* ********************** readFile2String *********************/

    /**
     * 读取文件到字符串中
     *
     * @param filePath 文件路径
     * @return 字符串
     */
    public static String readFile2String(final String filePath) {
        return readFile2String(FileUtils.getFileByPath(filePath), null);
    }

    /**
     * 读取文件到字符串中
     *
     * @param filePath    文件路径
     * @param charsetName 字符集名称
     * @return 字符串
     */
    public static String readFile2String(final String filePath, final String charsetName) {
        return readFile2String(FileUtils.getFileByPath(filePath), charsetName);
    }

    /**
     * 读取文件到字符串中
     *
     * @param file 文件
     * @return 字符串
     */
    public static String readFile2String(final File file) {
        return readFile2String(file, null);
    }

    /**
     * 读取文件到字符串中
     *
     * @param file        文件
     * @param charsetName 字符集名称
     * @return 字符串
     */
    public static String readFile2String(final File file, final String charsetName) {
        byte[] bytes = readFile2BytesByStream(file);
        if (bytes == null) return null;
        if (StringUtils.isSpace(charsetName)) {
            return new String(bytes);
        } else {
            try {
                return new String(bytes, charsetName);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return "";
            }
        }
    }

    /* ********************** readFile2BytesByStream *********************/

    /**
     * 读取文件到字节数组中
     *
     * @param filePath 文件路径
     * @return byte[]
     */
    public static byte[] readFile2BytesByStream(final String filePath) {
        return readFile2BytesByStream(FileUtils.getFileByPath(filePath), null);
    }

    /**
     * 读取文件到字节数组中
     *
     * @param file 文件
     * @return byte[]
     */
    public static byte[] readFile2BytesByStream(final File file) {
        return readFile2BytesByStream(file, null);
    }

    /**
     * 读取文件到字节数组中
     *
     * @param filePath 文件路径
     * @param listener 进度更新回调
     * @return byte[]
     */
    public static byte[] readFile2BytesByStream(final String filePath, final OnProgressUpdateListener listener) {
        return readFile2BytesByStream(FileUtils.getFileByPath(filePath), listener);
    }

    /**
     * 读取文件到字节数组中
     *
     * @param file     The file.
     * @param listener 进度更新回调
     * @return byte[]
     */
    public static byte[] readFile2BytesByStream(final File file, final OnProgressUpdateListener listener) {
        if (!FileUtils.isFileExists(file)) {
            return null;
        }
        try {
            ByteArrayOutputStream os = null;
            InputStream is = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE);
            try {
                os = new ByteArrayOutputStream();
                byte[] b = new byte[BUFFER_SIZE];
                int len;
                if (listener == null) {
                    while ((len = is.read(b, 0, BUFFER_SIZE)) != -1) {
                        os.write(b, 0, len);
                    }
                } else {
                    double totalSize = is.available();
                    int curSize = 0;
                    listener.onProgressUpdate(0);
                    while ((len = is.read(b, 0, BUFFER_SIZE)) != -1) {
                        os.write(b, 0, len);
                        curSize += len;
                        listener.onProgressUpdate(curSize / totalSize);
                    }
                }
                return os.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (os != null) {
                        os.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    public interface OnProgressUpdateListener {
        void onProgressUpdate(double progress);
    }
}
